/*
 * BLLib.Configuration.IniFile
 * Andy Tidball
 * 
 * Project: Black Lightning General Library
 * Copyright: GNU General Public License
 */

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace BLLib.Configuration {
	/// <summary>
	/// Encapsulates an INI file.
	/// </summary>
	public class IniFile {
		/// <summary>
		/// Parses the given string as the contents of an INI file.
		/// </summary>
		/// <param name="Contents">The string to parse as if it was the contents of an INI file.</param>
		/// <returns>A dictionary that maps section names to name/value pairs.</returns>
		public static Dictionary<string, Dictionary<string, string>> Parse(string Contents) {
			// create our output
			Dictionary<string, Dictionary<string, string>> Output = new Dictionary<string, Dictionary<string, string>>();

			// split the contents into lines and run through each
			string CurrentName   = null;
			Dictionary<string, string> CurrentValues = null;
			string[] Lines = Contents.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string Line in Lines) {
				// trim the line
				string TrimmedLine = Line.Trim();
				if (TrimmedLine == string.Empty) {
					continue;
				}

				// check if this is a new section
				if (TrimmedLine.StartsWith("[")) {
					// finish the old section
					if (CurrentValues != null) {
						Output.Add(CurrentName, CurrentValues);
					}

					// start a new section
					CurrentValues = new Dictionary<string, string>();
					CurrentName = TrimmedLine.Trim('[', ']');
				} else {
					// split the line into a name/value pair
					string[] Pair = TrimmedLine.Split(new char[] { '=' }, 2);
					if (Pair.Length < 2) {
						continue;
					}

					// add the name/value pair to the current section
					if (CurrentValues != null) {
						CurrentValues.Add(Pair[0], Pair[1]);
					}
				}
			}

			// add the last section to the output
			if (CurrentValues != null) {
				Output.Add(CurrentName, CurrentValues);
			}

			return Output;
		}

		// member data
		Dictionary<string, Dictionary<string,string>> _Sections; // the sections in the INI file (Section => (Name => Value))

		/// <summary>
		/// Loads an INI file from the given filename.
		/// </summary>
		/// <param name="Filename">The filename to load an INI file from.</param>
		public IniFile(string Filename) {
			// read in the file
			StreamReader In = new StreamReader(Filename);
			string Contents = In.ReadToEnd();
			In.Close();
			_Sections = IniFile.Parse(Contents);
		}

		/// <summary>
		/// Gets a dictionary of all the sections and settings in the file.  The dictionary maps section names to a dictionary containing name/value pairs from the section.
		/// </summary>
		public Dictionary<string, Dictionary<string, string>> Sections {
			get {
				return _Sections;
			}
		}

		/// <summary>
		/// Gets or sets the dictionary of name/value pairs in the given section of the INI file.  When setting the given section name is added if it doesn't yet exist.
		/// </summary>
		/// <param name="Section">The section of the INI file to get or set.</param>
		/// <returns>A dictionary of name/value pairs in the given section, or null if that section is not in the file.</returns>
		public Dictionary<string, string> this[string Section] {
			get {
				if (_Sections.ContainsKey(Section)) {
					return _Sections[Section];
				} else {
					return null;
				}
			}
			set {
				if (_Sections.ContainsKey(Section)) {
					_Sections.Add(Section, value);
				} else {
					_Sections[Section] = value;
				}
			}
		}

		/// <summary>
		/// Gets or sets the specified named value from the given section.  When setting a value the given section and name are added if they don't yet exist.
		/// </summary>
		/// <param name="Section">The section to get or set a value in.</param>
		/// <param name="Name">The name of the value to get or set.</param>
		/// <returns>The requested value, or null if either the section or name didn't exist in the file.</returns>
		public string this[string Section, string Name] {
			get {
				if (!_Sections.ContainsKey(Section)) {
					return null;
				}
				if (!_Sections[Section].ContainsKey(Name)) {
					return null;
				}
				return _Sections[Section][Name];
			}
			set {
				if (!_Sections.ContainsKey(Section)) {
					_Sections.Add(Section, new Dictionary<string, string>());
				}
				if (!_Sections[Section].ContainsKey(Name)) {
					_Sections[Section].Add(Name, value);
				} else {
					_Sections[Section][Name] = value;
				}
			}
		}

		/// <summary>
		/// Saves the IniFile to the given filename.
		/// </summary>
		/// <param name="Filename">The filename to save the IniFile to.</param>
		public void Save(string Filename) {
			StreamWriter Out = new StreamWriter(Filename);
			Out.Write(this.ToString());
			Out.Close();
		}

		/// <summary>
		/// Writes out the contents of the IniFile to a string.
		/// </summary>
		/// <returns>The contents of the IniFile.</returns>
		public override string ToString() {
			StringBuilder Output = new StringBuilder();

			// run through each section
			foreach (KeyValuePair<string, Dictionary<string, string>> Section in _Sections) {
				// write out the section's name
				Output.Append("[");
				Output.Append(Section.Key);
				Output.Append("]");
				Output.Append(Environment.NewLine);

				// run through each name/value pair in the section
				foreach (KeyValuePair<string, string> Pair in Section.Value) {
					// write out the pair
					Output.Append(Pair.Key);
					Output.Append("=");
					Output.Append(Pair.Value);
					Output.Append(Environment.NewLine);
				}
			}

			// return the result
			return Output.ToString();
		}
	}
}
